/*
XBaseTools
Copyright (C) 2008 Matthew Perry

This library/program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This libary/program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

using System;
using System.Collections.Generic;
using BaseTools.Configuration;
using BaseTools.Configuration.Parsers;

namespace SourceTools.IO
{
	
	/// <summary>
	/// Game info structure for the Source Engine
	/// </summary>
	public class SourceGameInfo
	{
		private string str_game;
		private bool bl_gamelogo;
		private string str_title, str_title2;
		private GameType gt_type;
		private bool bl_nodifficulty, bl_hasportals, bl_nocrosshair, bl_nohimodel, bl_nomodels;
		private string str_developer, str_developer_url, str_manual, str_icon;
		private List<string> lst_hidden_maps;
		private bool bl_nodegraph;
		private int int_SteamAppId, int_ToolsAppId;
		private List<SearchPath> lst_SearchPaths;
		
		/// <summary>
		/// Special AppIDs
		/// </summary>
		public const int APP_MOD = 215, APP_CSS = 240, APP_HL2 = 220, APP_ORANGEBOX = 218, APP_HL2DM = 300, APP_TOOLS = 211;
		
		/// <summary>
		/// The display name of the game in Steam
		/// </summary>
		public string Game
		{
			get
			{
				return str_game;
			}
			set
			{
				str_game = value;
			}
		}
		
		/// <summary>
		/// If true, resource/GameLogo.res will be displayed rather than the titles
		/// </summary>
		public bool GameLogo
		{
			get
			{
				return bl_gamelogo;
			}
			set
			{
				bl_gamelogo = value;
			}
		}
		
		/// <summary>
		/// The title displayed in the game menu
		/// </summary>
		public string Title
		{
			get
			{
				return str_title;
			}
			set
			{
				str_title = value;
			}
		}
		
		/// <summary>
		/// Omissible
		/// </summary>
		public string Title2
		{
			get
			{
				return str_title2;
			}
			set
			{
				str_title2 = value;
			}
		}
		
		/// <summary>
		/// Helps the GameUI to decide the menu configuration
		/// </summary>
		public GameType Type
		{
			get
			{
				return gt_type;
			}
			set
			{
				gt_type = value;
			}
		}
		
		/// <summary>
		/// Hides the Difficulty tab in Options
		/// </summary>
		public bool NoDifficulty
		{
			get
			{
				return bl_nodifficulty;
			}
			set
			{
				bl_nodifficulty = value;
			}
		}
		
		/// <summary>
		/// Shows the Portal tab in the Options panel
		/// </summary>
		public bool HasPortals
		{
			get
			{
				return bl_hasportals;
			}
			set
			{
				bl_hasportals = value;
			}
		}
		
		/// <summary>
		/// Disables the crosshair selection UI
		/// </summary>
		public bool NoCrosshair
		{
			get
			{
				return bl_nocrosshair;
			}
			set
			{
				bl_nocrosshair = value;
			}
		}
		
		/// <summary>
		/// Disables checkbox for cl_himodels
		/// </summary>
		public bool NoHiModel
		{
			get
			{
				return bl_nohimodel;
			}
			set
			{
				bl_nohimodel = value;
			}
		}
		
		/// <summary>
		/// Hides the model selection menu in the Options panel
		/// </summary>
		public bool NoModels
		{
			get
			{
				return bl_nomodels;
			}
			set
			{
				bl_nomodels = value;
			}
		}
		
		/// <summary>
		/// The developer's name
		/// </summary>
		public string Developer
		{
			get
			{
				return str_developer;
			}
			set
			{
				str_developer = value;
			}
		}
		
		/// <summary>
		/// The developer's website
		/// </summary>
		public string DeveloperURL
		{
			get
			{
				return str_developer_url;
			}
			set
			{
				str_developer_url = value;
			}
		}
		
		/// <summary>
		/// A 16x16 TGA icon representing the game
		/// </summary>
		public string Icon
		{
			get
			{
				return str_icon;
			}
			set
			{
				str_icon = value;
			}
		}
		
		/// <summary>
		/// A url pointing to the game manual
		/// </summary>
		public string Manual
		{
			get
			{
				return str_manual;
			}
			set
			{
				str_manual = value;
			}
		}
		
		/// <summary>
		/// List of maps that are not shown in the interface
		/// </summary>
		public List<string> HiddenMaps
		{
			get
			{
				return lst_hidden_maps;
			}
			set
			{
				lst_hidden_maps = value;
			}
		}
		
		/// <summary>
		/// Automatically generate nodegraphs
		/// </summary>
		public bool Nodegraph
		{
			get
			{
				return bl_nodegraph;
			}
			set
			{
				bl_nodegraph = value;
			}
		}
		
		/// <summary>
		/// Specifies what game content is accessible to the game
		/// </summary>
		public int SteamAppId
		{
			get
			{
				return int_SteamAppId;
			}
			set
			{
				int_SteamAppId = value;
			}
		}
		
		/// <summary>
		/// Specifies what game content editing tools will load
		/// </summary>
		public int ToolsAppId
		{
			get
			{
				return int_ToolsAppId;
			}
			set
			{
				int_ToolsAppId = value;
			}
		}
		
		/// <summary>
		/// The paths that the game will mount
		/// </summary>
		public List<SearchPath> SearchPaths
		{
			get
			{
				return lst_SearchPaths;
			}
			set
			{
				lst_SearchPaths = value;
			}
		}
		
		/// <summary>
		/// Adds a search path to the list
		/// </summary>
		/// <param name="type">The type of search path</param>
		/// <param name="path">The path name</param>
		public void AddSearchPath(SearchPathType type, string path)
		{
			lst_SearchPaths.Add(new SearchPath(type, path));
		}
		
		/// <summary>
		/// Adds a search path to the list
		/// </summary>
		/// <param name="path">The path to add</param>
		public void AddSearchPath(SearchPath path)
		{
			lst_SearchPaths.Add(path);
		}
		
		/// <summary>
		/// Adds a hidden map to the list
		/// </summary>
		/// <param name="mapName">The name of the map to hide</param>
		public void AddHiddenMap(string mapName)
		{
			lst_hidden_maps.Add(mapName);
		}
		
		/// <summary>
		/// Generates a configuration from the current game information
		/// </summary>
		/// <returns>A GameInfo structure</returns>
		public ConfigList GenerateConfig()
		{
			return GenerateConfig(false);
		}
		
		/// <summary>
		/// Generates a configuration from the current game information
		/// </summary>
		/// <param name="generateAll">Generate all options even if they are set to the default</param>
		/// <returns>A GameInfo structure</returns>
		public ConfigList GenerateConfig(bool generateAll)
		{
			ConfigList gen = new ConfigList();
			ConfigList ginfo = new ConfigList();
			gen.AddPair(new ConfigPair("GameInfo", ginfo, true, true));
			
			ginfo.AddPair(new ConfigPair("game", this.Game, false, true));
			
			if (generateAll || this.GameLogo)
				ginfo.AddPair(new ConfigPair("gamelogo", bl2s(this.GameLogo), false, false));
			
			if (this.Title != null)
				ginfo.AddPair(new ConfigPair("title", this.Title, false, true));
			if (this.Title2 != null)
				ginfo.AddPair(new ConfigPair("title2", this.Title2, false, true));
			if (this.Type != GameType.Both)
				ginfo.AddPair(new ConfigPair("type", t2s(this.Type), false, false));
			if (generateAll || this.NoDifficulty)
				ginfo.AddPair(new ConfigPair("nodifficulty", bl2s(this.NoDifficulty), false, false));
			if (generateAll || this.HasPortals)
				ginfo.AddPair(new ConfigPair("hasportals", bl2s(this.HasPortals), false, false));
			if (generateAll || this.NoCrosshair)
				ginfo.AddPair(new ConfigPair("nocrosshair", bl2s(this.NoCrosshair), false, false));
			if (generateAll || this.NoHiModel)
				ginfo.AddPair(new ConfigPair("nohimodel", bl2s(this.NoHiModel), false, false));
			if (generateAll || this.NoModels)
				ginfo.AddPair(new ConfigPair("nomodels", bl2s(this.NoModels), false, false));
			if (this.Developer != null)
				ginfo.AddPair(new ConfigPair("developer", this.Developer, true, true));
			if (this.DeveloperURL != null)
				ginfo.AddPair(new ConfigPair("developer_url", this.DeveloperURL, true, true));
			if (this.Icon != null)
				ginfo.AddPair(new ConfigPair("icon", this.Icon, true, true));
			if (this.Manual != null)
				ginfo.AddPair(new ConfigPair("manual", this.Manual, true, true));
			if (generateAll || HiddenMaps.Count > 0)
				ginfo.AddPair(new ConfigPair("hidden_maps", ListHiddenMaps(this.HiddenMaps), false, true));
			if (generateAll || !Nodegraph)
				ginfo.AddPair(new ConfigPair("nodegraph", bl2s(this.Nodegraph), false, false));
			
			ConfigList fsys = new ConfigList();
			ginfo.AddPair(new ConfigPair("FileSystem", fsys, false, true));
			
			fsys.AddPair(new ConfigPair("SteamAppId", SteamAppId.ToString(), false, false));
			fsys.AddPair(new ConfigPair("ToolsAppId", ToolsAppId.ToString(), false, false));
			
			if (generateAll || SearchPaths.Count > 0)
				fsys.AddPair(new ConfigPair("SearchPaths", ListSearchPaths(this.SearchPaths), false, true));
			
			return gen;
		}
		
		private static ConfigList ListSearchPaths(List<SearchPath> lst)
		{
			ConfigList ol = new ConfigList();
			
			foreach(SearchPath p in lst)
			{
				ol.AddPair(new ConfigPair(spt2s(p.Type), p.Path, false, false));
			}
			
			return ol;
		}
		private static ConfigList ListHiddenMaps(List<string> lst)
		{
			ConfigList ol = new ConfigList();
			
			foreach(string s in lst)
			{
				ol.AddPair(new ConfigPair(s, "1", true, false));
			}
			
			return ol;
		}
		
		private static string bl2s(bool b)
		{
			return b ? "1" : "0";
		}
		
		/// <summary>
		/// Creates a new SourceGameInfo
		/// </summary>
		public SourceGameInfo()
		{
			DefaultSettings();
		}
		
		/// <summary>
		/// Creates a new SourceGameInfo
		/// </summary>
		/// <param name="gameInfoFile">The gameinfo.txt path</param>
		public SourceGameInfo(string gameInfoFile)
		{
			ReadList(new SourceConfig().LoadConfiguration(gameInfoFile));
		}
		
		/// <summary>
		/// Creates a new SourceGameInfo
		/// </summary>
		/// <param name="gameInfoList"></param>
		public SourceGameInfo(ConfigList gameInfoList)
		{
			ReadList(gameInfoList);
		}
		private void DefaultSettings()
		{
			str_game = "Steam Game Title";
			bl_gamelogo = false;
			str_title = "Game Title";
			str_title2 = "";
			gt_type = GameType.Both;
			bl_nodifficulty = false;
			bl_hasportals = false;
			bl_nocrosshair = false;
			bl_nohimodel = false;
			bl_nomodels = false;
			str_developer = "Developer";
			str_developer_url = "Developer URL";
			str_manual = "Manual";
			str_icon = "";
			lst_hidden_maps = new List<string>();
			bl_nodegraph = true;
			int_SteamAppId = APP_MOD;
			int_ToolsAppId = APP_TOOLS;
			lst_SearchPaths = new List<SearchPath>();
		}
		private static bool s2b(string s)
		{
			return s != null && s != "0";
		}
		private void ReadList(ConfigList gi)
		{
			ConfigList gameInfo = gi.GetSection("GameInfo", false);
			ConfigList fileSystem;
			
			if (gameInfo != null)
			{
				str_game = gameInfo["game"];
				bl_gamelogo = s2b(gameInfo["gamelogo"]);
				str_title = gameInfo["title"];
				str_title2 = gameInfo["title2"];
				gt_type = StringGame(gameInfo["type"]);
				bl_nodifficulty = s2b(gameInfo["nodifficulty"]);
				bl_hasportals = s2b(gameInfo["hasportals"]);
				bl_nocrosshair = s2b(gameInfo["nocrosshair"]);
				bl_nohimodel = s2b(gameInfo["nohimodel"]);
				bl_nomodels = s2b(gameInfo["nomodels"]);
				str_developer = gameInfo["developer"];
				str_developer_url = gameInfo["developer_url"];
				str_manual = gameInfo["manual"];
				str_icon = gameInfo["icon"];
				lst_hidden_maps = GenerateHiddenMaps(gameInfo.GetSection("hidden_maps", false));
				bl_nodegraph = (gameInfo["nodegraph"] != "0");
				
				fileSystem = gameInfo.GetSection("FileSystem", false);
				
				if (fileSystem != null)
				{
					int_SteamAppId = ParseInt(fileSystem["SteamAppId"]);
					int_ToolsAppId = ParseInt(fileSystem["ToolsAppId"]);
					lst_SearchPaths = GenerateSearchPaths(fileSystem.GetSection("SearchPaths", false));
				}
				else
				{
					int_SteamAppId = 0;
					int_ToolsAppId = 0;
					lst_SearchPaths = new List<SearchPath>();
				}
			}
			else
			{
				DefaultSettings();
			}
		}
		
		/// <summary>
		/// Generates the search paths
		/// </summary>
		/// <param name="lst">The SearchPaths section of gameinfo.txt</param>
		/// <returns>The new search path list</returns>
		public static List<SearchPath> GenerateSearchPaths(ConfigList lst)
		{
			List<SearchPath> sp = new List<SearchPath>();
			
			foreach(ConfigPair p in lst.GetList())
			{
				if (p.Key.ToLower() == "game")
					sp.Add(new SearchPath(SearchPathType.GamePath, p.DataAsString));
				else if (p.Key.ToLower() == "testpathid")
					sp.Add(new SearchPath(SearchPathType.TestPath, p.DataAsString));
			}
			
			return sp;
		}
		private static string t2s(GameType t)
		{
			switch(t)
			{
				case GameType.Singleplayer:
					return "singleplayer_only";
				case GameType.Multiplayer:
					return "multiplayer_only";
				case GameType.Both:
					return null;
				default:
					return null;
			}
		}
		private static string spt2s(SearchPathType spt)
		{
			switch(spt)
			{
				case SearchPathType.GamePath:
					return "Game";
				case SearchPathType.TestPath:
					return "TestPathID";
				default:
					return null;
			}
		}
		private static int ParseInt(string s)
		{
			int i;
			if (int.TryParse(s, out i))
				return i;
			else
				return 0;
		}
		
		/// <summary>
		/// Generate the hidden maps list from the gameinfo.txt section
		/// </summary>
		/// <param name="lst">The hidden map section</param>
		/// <returns>A list of the hidden maps</returns>
		public static List<string> GenerateHiddenMaps(ConfigList lst)
		{
			List<string> hm = new List<string>();
			
			if (lst != null)
			{
				foreach(ConfigPair p in lst.GetList())
				{
					if (p.DataAsString != "0")
						hm.Add(p.Key);
				}
			}
			
			return hm;
		}
		private static GameType StringGame(string s)
		{
			if (s.ToLower() == "multiplayer_only")
				return GameType.Multiplayer;
			else if (s.ToLower() == "singleplayer_only")
				return GameType.Singleplayer;
			else
				return GameType.Both;
		}
	}
}
